﻿/*
 * framemodbusapdu.hpp
 *
 *  Created on: 2017年10月27日
 *      Author: Dylan.Gao
 */

#ifndef _DM_PROTOCOL_FRAMEMODBUSAPDU_HPP_
#define _DM_PROTOCOL_FRAMEMODBUSAPDU_HPP_

#include <dm/export.hpp>

#ifndef DM_API_PROTOCOL
#define DM_API_PROTOCOL DM_API_IMPORT
#endif

#include <dm/types.hpp>

namespace dm{
namespace protocol{

/**
 * Modbus应用数据单元
 */
class DM_API_PROTOCOL CFrameModbusApdu{
public:
	enum{
		MaxDataLen = 255,	// 缓冲区最大长度
	};

	/**
	 * 功能码
	 */
	enum EFunc{
		UnknowFunc 		 = 0x00,//!< 未知功能码。无效帧
		ReadCoilStatus  = 0x01,//!< ReadCoilStatus
		ReadInputStatus = 0x02,//!< ReadInputStatus
		ReadHoldReg     = 0x03,//!< ReadHoldReg
		ReadInputReg    = 0x04,//!< ReadInputReg
		ForceSigCoil    = 0x05,//!< ForceSigCoil
		PresetSigReg    = 0x06,//!< PresetSigReg
		ReadExcptStatus = 0x07,//!< ReadExcptStatus
		FetchEventCount = 0x0B,//!< FetchEventCount
		FetchEventLog   = 0x0C,//!< FetchEventLog
		ForceMtlCoil    = 0x0F,//!< ForceMtlCoil
		PresetMtlReg    = 0x10,//!< PresetMtlReg
		ReportSlaveId   = 0x11,//!< ReportSlaveId
		ReadGeneralRef  = 0x14,//!< ReadGeneralRef
		WriteGeneralRef = 0x15,//!< WriteGeneralRef
		MaskWrite4XReg  = 0x16,//!< MaskWrite4XReg
		ReadWrite4XReg  = 0x17,//!< ReadWrite4XReg
		ReadFifoQueue   = 0x18 //!< ReadFifoQueue
	};

	/**
	 * 错误码
	 */
	enum EErrCode{
		Err_None = 0x00, //!< 无错误
		Err_Func  = 0x01,//!< 功能码错误
		Err_Addr  = 0x02,//!< 地址错误
		Err_Value = 0x03,//!< 值错误
		Err_Slave = 0x04,//!< Err_Slave
		Err_Ack   = 0x05,//!< Err_Ack
		Err_Busy  = 0x06,//!< Err_Busy
		Err_Nack  = 0x07,//!< Err_Nack
		Err_Memo  = 0x08 //!< Err_Memo
	};

	/**
	 * 报文方向
	 */
	enum EFrameType{
		FtDn = 0,	// 下行报文
		FtUp,     	// 上行正常报文
		FtUpErr		// 上行错误报文
	};

	CFrameModbusApdu();
	CFrameModbusApdu( const CFrameModbusApdu& apdu );

	CFrameModbusApdu& operator=( const CFrameModbusApdu& apdu );

	inline const EFrameType& getFrameType()const{
		return m_frameType;
	}

	inline bool isDnFrame()const{
		return m_frameType==FtDn;
	}

	inline bool isUpFrame()const{
		return m_frameType!=FtDn;
	}

	inline bool isNormalUpFrame()const{
		return m_frameType==FtUp;
	}

	inline bool isErrorUpFrame()const{
		return m_frameType==FtUpErr;
	}

	inline void setFrameType( const EFrameType& ft ){
		m_frameType = ft;
	}

	inline void setDnFrame(){
		m_frameType = FtDn;
	}

	inline void setUpFrame(){
		m_frameType = FtUp;
	}

	inline void setUpErrorFrame(){
		m_frameType = FtUpErr;
	}

	/**
	 * 获取功能码
	 * @return
	 */
	inline const EFunc& getFunc()const{
		return m_fun;
	}

	/**
	 * 设置功能码
	 * @param func
	 */
	inline void setFunc( const EFunc& func ){
		m_fun = func;
	}

	/**
	 * 获取数据缓冲区长度
	 * @return
	 */
	inline const dm::uint8& getLen()const{
		return m_len;
	}

	/**
	 * 设置数据缓冲区长度
	 * @param len
	 */
	void setLen( const dm::uint8& len ){
		m_len = len;
	}

	/**
	 * 获取缓冲区
	 * @return
	 */
	inline const dm::uint8* getData()const{
		return m_data;
	}

	dm::int8 getData_int8( const dm::uint8& offset=0 )const;
	dm::uint8 getData_uint8( const dm::uint8& offset=0 )const;
	dm::int16 getData_int16( const dm::uint8& offset=0 )const;
	dm::uint16 getData_uint16( const dm::uint8& offset=0 )const;

	bool getData_bit( const int& bit,const dm::uint8& offset=0 )const;

	bool setData_int8( const dm::int8& c,const dm::uint8& offset=0 );
	bool setData_uint8( const dm::uint8& c,const dm::uint8& offset=0 );
	bool setData_int16( const dm::int16& c,const dm::uint8& offset=0 );
	bool setData_uint16( const dm::uint16& c,const dm::uint8& offset=0 );

	bool setData_bit( const bool& set,const int& bit,const dm::uint8& offset=0 );

	/**
	 * 填充缓冲区
	 * @param buf 数据
	 * @param size 数据长度
	 * @param offset 填充起始偏移
	 * @return
	 */
	bool setData( const dm::uint8* buf,const dm::uint8& size,const dm::uint8& offset=0 );
	bool setData( const dm::int8* buf,const dm::uint8& size,const dm::uint8& offset=0 );
	bool setData( const dm::uint16* buf,const dm::uint8& size,const dm::uint8& offset=0 );
	bool setData( const dm::int16* buf,const dm::uint8& size,const dm::uint8& offset=0 );

	//======= 应用处理：下行报文 ==========
	inline void setReadCoils( const dm::uint16& startAddress,const dm::uint16& quantity ){
		setDnReadCmd(ReadCoilStatus,startAddress,quantity);
	}

	inline void setReadDiscreteInputs( const dm::uint16& startAddress,const dm::uint16& quantity ){
		setDnReadCmd(ReadInputStatus,startAddress,quantity);
	}

	inline void setReadHoldingRegisters( const dm::uint16& startAddress,const dm::uint16& quantity ){
		setDnReadCmd(ReadHoldReg,startAddress,quantity);
	}

	inline void setReadInputRegisters( const dm::uint16& startAddress,const dm::uint16& quantity ){
		setDnReadCmd(ReadInputReg,startAddress,quantity);
	}

	void setWriteSingleCoil( const dm::uint16& address,const bool& on );

	void setWriteSingleRegister( const dm::uint16& address,const dm::uint16& value );

	/**
	 * 创建帧，写多个线圈。本函数并未设置数据，之后需要调用setWriteMultipleCoilsValue来设置
	 * @param startAddress
	 * @param quantity
	 */
	void setWriteMultipleCoils( const dm::uint16& startAddress,const dm::uint16& quantity );
	inline void setWriteMultipleCoilsValue( const int& index,const bool& set ){
		setData_bit(set,index,5);
	}

	void setWriteMultipleRegisters( const dm::uint16& startAddress,const dm::uint16& quantity );
	void setWriteMultipleRegistersValue( const int& index,const dm::uint16& output ){
		setData_uint16(output,5+index*2);
	}
	void setWriteMultipleRegisters( const dm::uint16& startAddress,const dm::uint16& quantity,const dm::uint16* output );
	void setWriteMultipleRegisters( const dm::uint16& startAddress,const dm::uint16& quantity,const dm::uint8* output );

	//======= 应用处理：上行报文 ==========
	bool getError( EErrCode& errCode )const;
	bool setError( const EErrCode& errCode );
	void clrError();

	//======= 响应 ========
	void respone( const CFrameModbusApdu& dnFrame,const EErrCode& errCode = Err_None );

	static inline int getBytes( const int& bits ){
		return getByteOffset(bits-1)+1;
	}

	static inline int getByteOffset( const int& bit ){
		return bit/8;
	}

	static inline int getBitOffset( const int& bit ){
		return bit%8;
	}

protected:
	bool setDnReadCmd( const EFunc& func,const dm::uint16& addr, const dm::uint16& size );

protected:
	EFrameType m_frameType;	// 报文类型
	EFunc m_fun;				// 功能码
	dm::uint8 m_len;	    // 缓冲区长度
	dm::uint8 m_data[MaxDataLen];	// 应用数据缓冲区
};

}
}

#endif /* DM_RUNTIME_INCLUDE_DM_PROTOCOL_FRAMEMODBUSAPDU_HPP_ */
